1 /**
2  * Utility code for path finding
3  *
4  * License:
5  *     D version of code is under MIT. The original is under Apache 2.0.
6  * 
7  *     The MIT License (MIT)
8  *
9  *     Copyright (c) 2014 Devisualization (Richard Andrew Cattermole)
10  *
11  *     Permission is hereby granted, free of charge, to any person obtaining a copy
12  *     of this software and associated documentation files (the "Software"), to deal
13  *     in the Software without restriction, including without limitation the rights
14  *     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15  *     copies of the Software, and to permit persons to whom the Software is
16  *     furnished to do so, subject to the following conditions:
17  *
18  *     The above copyright notice and this permission notice shall be included in all
19  *     copies or substantial portions of the Software.
20  *
21  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22  *     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  *     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24  *     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  *     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26  *     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27  *     SOFTWARE.
28  *
29  * See_Also:
30  *		http://www.redblobgames.com/pathfinding/a-star/implementation.html
31  */
32 module devisualization.util.algorithms.pathfinding.defs;
33 deprecated("Killing"):
34 
35 version(unittest) {
36 	struct XY {
37 		int x;
38 		int y;
39 	}
40 
41 	T from_id_width(T)(T v, T width) pure { return XY(id % width, id / width); }
42 	enum XY[] DIAGRAM1_WALLS = [21,22,51,52,81,82,93,94,111,112,123,124,133,134,141,142,153,154,163,164,171,172,173,174,175,183,184,193,194,201,202,203,204,205,213,214,223,224,243,244,253,254,273,274,283,284,303,304,313,314,333,334,343,344,373,374,403,404,433,434].from_id_width;
43 
44 	GridWithWeights!(XY, int) diagram4() {
45 		GridWithWeights!(XY, int) ret = GridWithWeights(10, 10);
46 		ret.walls = [XY(1, 7), XY(1, 8), XY(2, 7), XY(2, 8), XY(3, 7), XY(3, 8)];
47 		ret.weights = [(3, 4), (3, 5), (4, 1), (4, 2),
48 					   (4, 3), (4, 4), (4, 5), (4, 6), 
49 					   (4, 7), (4, 8), (5, 1), (5, 2),
50 					   (5, 3), (5, 4), (5, 5), (5, 6), 
51 					   (5, 7), (5, 8), (6, 2), (6, 3), 
52 					   (6, 4), (6, 5), (6, 6), (6, 7), 
53 					   (7, 3), (7, 4), (7, 5)];
54 		return ret;
55 	}
56 }
57 
58 /**
59  * Does the type have:
60  * - x
61  * - y
62  * - x and y being both the same type
63  * - Ability to be initiated like a struct (think opCall for classes)
64  *
65  * Params:
66  *     T	=	The type
67  *
68  * Returns:
69  *     If the value is compliant
70  */
71 bool hasXYPoints(T)() {
72 	return __traits(hasMember, T, "x") && __traits(hasMember, T, "y") &&
73            is(typeof(T.x) == typeof(T.y)) &&
74 		   __traits(compiles, { T v = T(typeof(T.x).init, typeof(T.y).init); });
75 }
76 
77 /**
78  * The distance between two points
79  * Type of parameters must abide by hasXYPoints
80  *
81  * Params:
82  *     a	=	The first position
83  *     b	=	The second position
84  *
85  * Returns:
86  *     The distance
87  *
88  * See_Also:
89  *     hasXYPoints
90  */
91 T heuristic(T, U = typeof(T.x))(T a, T b) if (hasXYPoints!T) {
92 	import std.math : abs;
93 	return cast(U)(abs(a.x - b.x) + abs(a.y - b.y));
94 }
95 
96 /**
97  * Turns the output from a search algorithm into a set path
98  *
99  * Params:
100  *    came_from		=	The positions between start and end
101  *    start			=	Starting position to go to
102  *    goal			=	The end position
103  *
104  * Returns:
105  *     A path from a position to another
106  *
107  * See_Also:
108  *     dijkstra_search, a_star_search, breadth_first_search
109  */
110 T[] reconstruct_path(T)(T[] came_from, T start, T goal) {
111 	T current = goal;
112 	T[] path = [current];
113 	
114 	while (current != start) {
115 		current = came_from[current];
116 		path ~= current;
117 	}
118 	
119 	return path;
120 }
121 
122 /**
123  * A simple graph structure
124  */
125 struct Graph(T=string) {
126 	private {
127 		T[][T] edges_;
128 	}
129 	
130 	@property {
131 		/**
132 		 * Get the edges this graph has
133 		 *
134 		 * Returns:
135 		 *     The edges of the graph
136 		 */
137 		T[][T] edges() { return edges_; }
138 		
139 		/**
140 		 * Set the edges this graph has
141 		 *
142 		 * Params:
143 		 *     value	=	The new edges of the graph
144 		 */
145 		void edges(T[][T] value) { edges_ = value; }
146 	}
147 	
148 	/**
149 	 * Get a set edge, neighbors
150 	 *
151 	 * Params:
152 	 *     The id of the edge
153 	 *
154 	 * Returns:
155 	 *     The neighbor edges or null if id doesn't exist
156      */
157 	T[] neighbors(T id) { return edges_.get(id, null); }
158 }
159 
160 /**
161  * A 2d grid that is rectangle in nature
162  *
163  * Params:
164  *    T	=	A point type with x and y fields
165  *    U	=	The type of x and y
166  */
167 struct SquareGrid(T, U = typeof(T.x)) if (hasXYPoints!T) {
168 	Graph this_;
169 	alias this_ this;
170 	
171 	private {
172 		U width_;
173 		U height_;
174 		
175 		T[] walls_;
176 	}
177 	
178 	/**
179 	 * Creates the grid given the size
180 	 *
181 	 * Params:
182 	 *     width	=	Width of the grid
183 	 *     height	=	Height of the grid
184 	 */
185 	this(U width, U height) {
186 		width_ = width;
187 		height_ = height;
188 	}
189 	
190 	/**
191 	 * Is a given position within the grid
192 	 *
193 	 * Params:
194 	 *     id	=	The position
195 	 *
196 	 * Returns:
197 	 *     If the position is within the grid
198      */
199 	bool in_bounds(T id) {
200 		return 0 <= id.x && id.x < width_ &&
201 			   0 <= id.y && id.y < height_;
202 	}
203 	
204 	/**
205 	 * Is a given position blocked by a wall
206 	 *
207 	 * Params:
208 	 *     id	=	The position
209 	 *
210 	 * Returns:
211 	 *     If the position is blocked by a wall
212      */
213 	bool passable(T id) {
214 		import std.algorithm : canFind;
215 		return walls_.canFind(id);
216 	}
217 	
218 	/**
219 	 * Get a set edge, neighbors
220 	 *
221 	 * Params:
222 	 *     id	=	The id of the edge
223 	 *
224 	 * Returns:
225 	 *     The neighbor edges or null if id doesn't exist
226      */
227 	string[] neighbors(T id) {
228 		import std.algorithm : reverse, filter, moveAll;
229 
230 		T[] results = [T(id.x + 1, id.y), T(id.x, id.y - 1), T(id.x - 1, id.y), T(id.x, id.y + y)];
231 		if ((id.x + id.y) % 2 == 0)
232 			results = results.reverse;
233 		
234 		filter!`in_bounds(a) && passable(a)`(results).moveAll(results);
235 		
236 		return results;
237 	}
238 }
239 
240 /**
241  * A grid that has weighting per node
242  *
243  * Params:
244  *    T	=	A point type with x and y fields
245  *    U	=	The type of x and y
246  */
247 struct GridWithWeights(T, U = ubyte, V = typeof(T.x)) {
248 	SquareGrid!(T, V) this_;
249 	alias this_ this;
250 	
251 	private {
252 		U[T] weights_;
253 	}
254 	
255 	/**
256 	 * Gets the weighting for an item
257 	 *
258 	 * Params:
259 	 *     a		=	Previous position
260 	 *     b		=	Current position
261 	 *     default_	=	Default value to return. Default: 1
262 	 */
263 	U cost(T a, T b, U default_ = 1) {
264 		return weights_.get(b, default_);
265 	}
266 }
267 
268 /**
269  * A simple wrap around a DList
270  *
271  * See_Also:
272  *    std.algorithm.DList
273  */
274 struct Queue(T) {
275 	DList!T this_;
276 	alias this_ this;
277 	
278 	/**
279 	 * Adds an item to the queue
280 	 *
281 	 * Params:
282 	 *     x	=	The value to add
283 	 */
284 	void put(T x) { this_ ~= x; }
285 	
286 	/**
287 	 * Gets the front item (FIFO) of the queue
288 	 *
289 	 * Returns:
290 	 *     The first item in queue
291 	 */
292 	T get() {
293 		T ret = this_.opSlice().front;
294 		this_.opSlice().popFront;
295 		return ret;
296 	}
297 }
298 
299 /**
300  * A wrapper around BinaryHeap
301  * Sorts a set of items based upon a priority
302  *
303  * Params:
304  *    T	=	The queue item type
305  *    U	=	The priority type
306  *
307  * See_Also:
308  *     std.collections.binaryheap.BinaryHeap
309  */
310 struct PriorityQueue(T, U=ubyte) {
311 	struct PriorityQueueItem {
312 		T item;
313 		U priority;
314 	}
315 	
316 	BinaryHeap!(PriorityQueueItem, `a.priority < b.priority`) elements;
317 	alias elements this;
318 	
319 	/**
320 	 * Adds an item to the queue
321 	 *
322 	 * Params:
323 	 *     item		=	The value to add
324 	 *     priority	=	The priority of the item
325 	 */
326 	void put(T item, U priority) { elements ~= PriorityQueueItem(item, priority); }
327 	
328 	/**
329 	 * Gets the front item (FIFO) of the queue
330 	 *
331 	 * Returns:
332 	 *     The first item in queue sorted based upon the priority
333 	 */
334 	U get() {
335 		T ret = this_.opSlice().front;
336 		this_.opSlice().popFront;
337 		return ret;
338 	}
339 }